package com.huidu.bitrade.controller;


import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.google.common.collect.Lists;
import com.huidu.bitrade.component.CoinExchangeRate;
import com.huidu.bitrade.constant.SysConstant;
import com.huidu.bitrade.entity.*;
import com.huidu.bitrade.entity.*;
import com.huidu.bitrade.processor.CoinProcessor;
import com.huidu.bitrade.processor.CoinProcessorFactory;
import com.huidu.bitrade.service.*;
import com.huidu.bitrade.service.CoinService;
import com.huidu.bitrade.service.ExchangeCoinService;
import com.huidu.bitrade.service.ExchangeTradeService;
import com.huidu.bitrade.util.MessageResult;

import com.huidu.bitrade.service.MarketService;
import com.huidu.bitrade.util.RedisUtilUse;
import com.huidu.bitrade.vo.CoinThumbVO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;

@Slf4j
@RestController
public class MarketController {
    @Autowired
    private MarketService marketService;
    @Autowired
    private ExchangeCoinService coinService;
    @Autowired
    private CoinService coinInfoService;
    @Autowired
    private CoinProcessorFactory coinProcessorFactory;
    @Autowired
    private ExchangeTradeService exchangeTradeService;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private CoinExchangeRate coinExchangeRate;
    @Autowired
    private RedisUtilUse redisUtilUse;

    /**
     * 获取支持的交易币种
     *
     * @return
     */
    @RequestMapping("symbol")
    public List<ExchangeCoin> findAllSymbol() {
        List<ExchangeCoin> coins = coinService.findAllVisible();
        return coins;
    }

    @RequestMapping("overview")
    public Map<String, List<CoinThumb>> overview() {
        Map<String, List<CoinThumb>> result = new HashMap<>();
        List<ExchangeCoin> recommendCoin = coinService.findAllByFlag(1);
        List<CoinThumb> recommendThumbs = new ArrayList<>();
        for (ExchangeCoin coin : recommendCoin) {
            CoinProcessor processor = coinProcessorFactory.getProcessor(coin.getSymbol());
            if (processor != null) {
                CoinThumb thumb = processor.getThumb();
                recommendThumbs.add(thumb);
            }
        }
        result.put("recommend", recommendThumbs);
        List<CoinThumb> allThumbs = findSymbolThumb();
        Collections.sort(allThumbs, (o1, o2) -> o1.getChg().compareTo(o2.getChg()));

        result.put("changeRank", getHeadTail(allThumbs, true, x -> x.getChg().signum() > 0, 10)); // 涨幅前10名
        result.put("changeRankDown", getHeadTail(allThumbs, false, x -> x.getChg().signum() < 0, 10)); // 跌幅前10名

        // 成交量前10名
        Collections.sort(allThumbs, (o1, o2) -> o1.getTurnover().compareTo(o2.getTurnover()));
        result.put("turnoverRank", getHeadTail(allThumbs, true, x -> x.getTurnover().signum() > 0, 10));
        return result;
    }

    // getHeadTail 从数组中到到尾 或者从尾到头找连续的满足条件 cond 的元素
    private List<CoinThumb> getHeadTail(List<CoinThumb> thumbs, boolean tail, Predicate<CoinThumb> cond, int limit) {
        int cur = tail ? thumbs.size() - 1 : 0;
        int end = tail ? -1 : thumbs.size();
        int delta = tail ? -1 : 1;
        List<CoinThumb> ret = new ArrayList<>();
        while (cur != end && ret.size() < limit && cond.test(thumbs.get(cur))) {
            ret.add(thumbs.get(cur));
            cur += delta;
        }
        return ret;
    }

    @RequestMapping("coinThumb")
    public CoinThumb coinThumb(String symbol) {
        CoinThumb thumb = null;
        CoinProcessor processor = coinProcessorFactory.getProcessor(symbol);
        if (processor != null) {
            thumb = processor.getThumb();
        }
        return thumb;
    }

    @RequestMapping("engines")
    public Map<String, Integer> engines() {
        Map<String, CoinProcessor> processorList = coinProcessorFactory.getProcessorMap();
        Map<String, Integer> symbols = new HashMap<String, Integer>();
        processorList.forEach((key, processor) -> {
            if (processor.isStopKline()) {
                symbols.put(key, 2);
            } else {
                symbols.put(key, 1);
            }
        });
        return symbols;
    }

    /**
     * 获取币种详情
     *
     * @param symbol
     * @return
     */
    @RequestMapping("coin-info")
    public Coin findCoin(String unit) {
        Coin coin = coinInfoService.findByUnit(unit);
        coin.setColdWalletAddress("");//隐藏冷钱包地址
        return coin;
    }

    /**
     * 获取C2C中USDT兑换人民币价格
     *
     * @param symbol
     * @return
     */
    @RequestMapping("ctc-usdt")
    public MessageResult ctcUsdt() {
        MessageResult mr = new MessageResult(0, "success");
        BigDecimal latestPrice = coinExchangeRate.getUsdtCnyRate();
        JSONObject obj = new JSONObject();
        obj.put("buy", latestPrice);
        // 0.015为1.5%的买卖差价
        obj.put("sell", latestPrice.subtract(latestPrice.multiply(new BigDecimal(0.011)).setScale(2, BigDecimal.ROUND_DOWN)));
        mr.setData(obj);
        return mr;
    }

    /**
     * 获取某交易对详情
     *
     * @param symbol
     * @return
     */
    @RequestMapping("symbol-info")
    public ExchangeCoin findSymbol(String symbol) {
        ExchangeCoin coin = coinService.findBySymbol(symbol);
        coin.setCurrentTime(Calendar.getInstance().getTimeInMillis());
        return coin;
    }

    /**
     * 获取币种缩略行情
     *
     * @return
     */
    @RequestMapping("symbol-thumb")
    public List<CoinThumb> findSymbolThumb() {
        List<ExchangeCoin> coins = coinService.findAllVisible();
        List<CoinThumb> thumbs = new ArrayList<>();
        for (ExchangeCoin coin : coins) {
            CoinProcessor processor = coinProcessorFactory.getProcessor(coin.getSymbol());
            CoinThumb thumb = processor.getThumb();
            if(coin.getBaseSymbol().equals("CNHD")){
                BigDecimal baseUsdRate = coinExchangeRate.getUsdRate(coin.getBaseSymbol());
                thumb.setTurnover(thumb.getTurnover().multiply(baseUsdRate).setScale(4, RoundingMode.UP));
            }
            thumb.setZone(coin.getZone());
            thumbs.add(thumb);
        }
        return thumbs;
    }

    private static List<String> signList;

    static {
        signList = Lists.newArrayList();
        signList.add("ur2us7janc3pdf");
        signList.add("ur2us63gda3pdf");
        signList.add("ur2us63gda3cfp");
    }

    /**
     * 获取指定交易对行情
     * @param sign
     * @param symbol
     * @return
     */
    @GetMapping("findSymbolThumb")
    public MessageResultVo<List<CoinThumbVO>> findSymbolThumbVO(@RequestParam("sign") String sign,@RequestParam("symbol") String symbol) {
        if(!signList.contains(sign)){
            return MessageResultVo.error("签名不正确");
        }
        String check = redisUtilUse.getCacheObject("interface:check:"+sign);
        if(StringUtils.isNotEmpty(check)){
            return MessageResultVo.error("请求太频繁，请稍后再试");
        }
        redisTemplate.opsForValue().set("interface:check:"+sign,"value",5, TimeUnit.SECONDS);

        String serviceName = "bitrade-market";
        String url = "http://" + serviceName + "/market/symbol-thumb";
        ResponseEntity<JSONArray> forEntity = restTemplate.getForEntity(url, JSONArray.class);
        List<CoinThumbVO> list = Lists.newArrayList();
        JSONArray body = forEntity.getBody();
        for (int i = 0; i < body.size(); i++) {
            JSONObject jsonObject = body.getJSONObject(i);
            if(symbol.equals(jsonObject.getString("symbol"))){
                CoinThumbVO coinThumbVO = JSONObject.parseObject(jsonObject.toJSONString(), CoinThumbVO.class);
                list.add(coinThumbVO);
                break;
            }
        }
        return MessageResultVo.success(list);
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class MessageResultVo<T> {

        private int code;

        private String message;

        private T data;

        public static MessageResultVo error(String msg){
            return new MessageResultVo(500,msg,"");
        }

        public static <T> MessageResultVo success(T data){
            return new MessageResultVo(0,"success",data);
        }
    }

    /**
     * 获得指定货币的当前行情
     *
     * @param symbol
     * @return
     */
    @RequestMapping("getSymbolThumb")
    public CoinThumb getSymbolThumb(String symbol) {
        CoinProcessor processor = coinProcessorFactory.getProcessor(symbol);
        if (Objects.isNull(processor)) {
            return null;
        }
        return processor.getThumb();
    }

    @RequestMapping("symbol-thumb-trend")
    public JSONArray findSymbolThumbWithTrend() {
        List<ExchangeCoin> coins = coinService.findAllVisible();
        //List<CoinThumb> thumbs = new ArrayList<>();
        Calendar calendar = Calendar.getInstance();
        //将秒、微秒字段置为0
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        calendar.set(Calendar.MINUTE, 0);
        long nowTime = calendar.getTimeInMillis();
        calendar.add(Calendar.HOUR_OF_DAY, -24);
        JSONArray array = new JSONArray();
        long firstTimeOfToday = calendar.getTimeInMillis();
        for (ExchangeCoin coin : coins) {
            CoinProcessor processor = coinProcessorFactory.getProcessor(coin.getSymbol());
            CoinThumb thumb = processor.getThumb();
            JSONObject json = (JSONObject) JSON.toJSON(thumb);
            json.put("zone", coin.getZone());
            List<KLine> lines = marketService.findAllKLine(thumb.getSymbol(), firstTimeOfToday, nowTime, "15min");
            JSONArray trend = new JSONArray();
            for (KLine line : lines) {
                trend.add(line.getClosePrice());
            }
            json.put("trend", trend);
            array.add(json);
        }
        return array;
    }

    /**
     * 获取币种历史K线
     *
     * @param symbol
     * @param from
     * @param to
     * @param resolution
     * @return
     */
    @RequestMapping("history")
    public JSONArray findKHistory(String symbol, long from, long to, String resolution) {
        String period = "";
        if (resolution.endsWith("H") || resolution.endsWith("h")) {
            period = resolution.substring(0, resolution.length() - 1) + "hour";
        } else if (resolution.endsWith("D") || resolution.endsWith("d")) {
            period = resolution.substring(0, resolution.length() - 1) + "day";
        } else if (resolution.endsWith("W") || resolution.endsWith("w")) {
            period = resolution.substring(0, resolution.length() - 1) + "week";
        } else if (resolution.endsWith("M") || resolution.endsWith("m")) {
            period = resolution.substring(0, resolution.length() - 1) + "month";
        } else {
            Integer val = Integer.parseInt(resolution);
            if (val < 60) {
                period = resolution + "min";
            } else {
                period = (val / 60) + "hour";
            }
        }
        List<KLine> list = marketService.findAllKLine(symbol, from, to, period);

        JSONArray array = new JSONArray();
        boolean startFlag = false;
        KLine temKline = null;
        for (KLine item : list) {
            // 此段处理是过滤币种开头出现0开盘/收盘的K线
            if (!startFlag && item.getOpenPrice().compareTo(BigDecimal.ZERO) == 0) {
                continue;
            } else {
                startFlag = true;
            }
            // 中间段如果出现为0的现象，需要处理一下
            if (item.getOpenPrice().compareTo(BigDecimal.ZERO) == 0) {
                item.setOpenPrice(temKline.getClosePrice());
                item.setClosePrice(temKline.getClosePrice());
                item.setHighestPrice(temKline.getClosePrice());
                item.setLowestPrice(temKline.getClosePrice());
            }
            JSONArray group = new JSONArray();
            group.add(0, item.getTime());
            group.add(1, item.getOpenPrice());
            group.add(2, item.getHighestPrice());
            group.add(3, item.getLowestPrice());
            group.add(4, item.getClosePrice());
            group.add(5, item.getVolume());
            array.add(group);

            temKline = item;
        }
        return array;
    }

    /**
     * 查询最近成交记录
     *
     * @param symbol 交易对符号
     * @param size   返回记录最大数量
     * @return
     */
    @RequestMapping("latest-trade")
    public List<ExchangeTrade> latestTrade(String symbol, int size) {
        return exchangeTradeService.findLatest(symbol, size);
    }

    @RequestMapping("exchange-plate")
    public Map<String, List<TradePlateItem>> findTradePlate(String symbol) {
        //远程RPC服务URL,后缀为币种单位
        String serviceName = "service-exchange-trade";
        String url = "http://" + serviceName + "/monitor/plate?symbol=" + symbol;
        ResponseEntity<HashMap> result = restTemplate.getForEntity(url, HashMap.class);
        return (Map<String, List<TradePlateItem>>) result.getBody();
    }


    @RequestMapping("exchange-plate-mini")
    public Map<String, JSONObject> findTradePlateMini(String symbol) {
        //远程RPC服务URL,后缀为币种单位
        String serviceName = "service-exchange-trade";
        String url = "http://" + serviceName + "/monitor/plate-mini?symbol=" + symbol;
        ResponseEntity<HashMap> result = restTemplate.getForEntity(url, HashMap.class);
        return (Map<String, JSONObject>) result.getBody();
    }


    @RequestMapping("exchange-plate-full")
    public Map<String, JSONObject> findTradePlateFull(String symbol) {
        //远程RPC服务URL,后缀为币种单位
        String serviceName = "service-exchange-trade";
        String url = "http://" + serviceName + "/monitor/plate-full?symbol=" + symbol;
        ResponseEntity<HashMap> result = restTemplate.getForEntity(url, HashMap.class);
        return (Map<String, JSONObject>) result.getBody();
    }

    @GetMapping("add_dcitionary/{bond}/{value}")
    public MessageResult addDcitionaryForAdmin(@PathVariable("bond") String bond, @PathVariable("value") String value) {
        log.info(">>>>字典表数据已修改>>>修改缓存中的数据>>>>>bond>" + bond + ">>>>>value>>" + value);
        String key = SysConstant.DATA_DICTIONARY_BOUND_KEY + bond;
        ValueOperations valueOperations = redisTemplate.opsForValue();
        Object bondvalue = valueOperations.get(key);
        if (bondvalue == null) {
            log.info(">>>>>>缓存中无数据>>>>>");
            valueOperations.set(key, value);
        } else {
            log.info(">>>>缓存中有数据>>>");
            valueOperations.getOperations().delete(key);
            valueOperations.set(key, value);
        }
        MessageResult re = new MessageResult();
        re.setCode(0);
        re.setMessage("success");
        return re;
    }

    /**
     * BTC/USDT趋势线
     *
     * @return
     */
    @RequestMapping("/btc/trend")
    public MessageResult btcTrend() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        calendar.set(Calendar.MINUTE, 0);
        long nowTime = calendar.getTimeInMillis();
        calendar.add(Calendar.HOUR_OF_DAY, -24);

        JSONArray array = new JSONArray();
        long firstTimeOfToday = calendar.getTimeInMillis();

        List<KLine> lines = marketService.findAllKLine("BTC/USDT", firstTimeOfToday, nowTime, "5min");
        JSONArray trend = new JSONArray();
        for (KLine line : lines) {
            trend.add(line.getClosePrice());
        }
        MessageResult re = new MessageResult();
        re.setCode(0);
        re.setData(trend);
        re.setMessage("success");
        return re;
    }

}
